package com.fare.taluo.ftp.channel;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

import com.fare.taluo.ftp.DownloadStatus;
import com.fare.taluo.ftp.FTPService;
import com.fare.taluo.ftp.FtpConfiguration;
import com.fare.taluo.ftp.UploadStatus;
import com.fare.taluo.ftp.exception.FTPDownloadException;
import com.fare.taluo.ftp.exception.FTPUploadException;

/**
 * FTP连接,对apache下组件commons-net包中FTPClient的封装
 * 
 * @author xiangsf 2012-12-6
 * 
 */
public class FTPChannel implements FTPService {
	protected Logger logger = Logger.getLogger(getClass());

	public FTPClient ftpClient = new FTPClient();

	private FtpConfiguration ftpConfig;

	public FTPChannel() {
		// 设置将过程中使用到的命令输出到控制台
		if (logger.isInfoEnabled()) {
			this.ftpClient.addProtocolCommandListener(new PrintCommandListener(
					new PrintWriter(System.out)));
		}
	}

	public FTPChannel(FtpConfiguration ftpConfig) {
		this.ftpConfig = ftpConfig;
		// 设置将过程中使用到的命令输出到控制台
		if (logger.isInfoEnabled()) {
			this.ftpClient.addProtocolCommandListener(new PrintCommandListener(
					new PrintWriter(System.out)));
		}
	}

	public FTPChannel connect(FtpConfiguration ftpConfig)
			throws SocketException, IOException {
		this.ftpConfig = ftpConfig;
		return connect();
	}

	public FTPChannel connect() throws SocketException, IOException {
		ftpClient.connect(ftpConfig.getFtpHost(), ftpConfig.getFtpPort());
		ftpClient.setControlEncoding(ftpConfig.getDefaultLocalCharset());
		if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
			if (ftpClient.login(ftpConfig.getUsername(),
					ftpConfig.getPassword())) {
				return this;
			}
		}
		logger.info("ftp " + ftpConfig.getFtpHost() + "  fail!");
		disconnect();
		throw new SocketException("ftp " + ftpConfig.getFtpHost() + "  fail!");
	}

	/**
	 * 断开与远程服务器的连接
	 * 
	 * @throws IOException
	 */
	public void disconnect() throws IOException {
		if (ftpClient.isConnected()) {
			ftpClient.logout();
			ftpClient.disconnect();
		}
	}

	private void setFtpClientMode() throws IOException {
		logger.info("ftp工作目录：" + ftpClient.printWorkingDirectory());
		// 设置被动模式
		ftpClient.enterLocalPassiveMode();
		// 设置以二进制方式传输
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
	}

	/**
	 * 下载文件
	 * 
	 * @param remoteFile
	 *            远程文件全路径
	 * @param localDir
	 *            本地目录路径
	 * @return
	 * @throws IOException
	 */
	public DownloadStatus downloadFile(String[] remoteFile, String localDir)
			throws FTPDownloadException {
		try {
			this.setFtpClientMode();

			FTPFile[] files = null;
			File f = null;
			FileOutputStream out = null;
			int downloadCount = 0; // 下载成功的计数器
			String formatRemoteFileName = null;
			try {
				if (remoteFile == null || remoteFile.length == 0) {
					logger.error("没有需要下载的远程文件");
					return DownloadStatus.Remote_File_Noexist;
				}
				for (String rs : remoteFile) {
					try {
						formatRemoteFileName = new String(
								rs.getBytes(this.ftpConfig
										.getDefaultLocalCharset()),
								this.ftpConfig.getDefaultRemoteCharset());
						files = ftpClient.listFiles(formatRemoteFileName);
						// 没有文件
						if (files == null || files.length != 1) {
							logger.error("远程文件" + rs + "不存在或非文件,跳过");
							continue;
						}
						// 本地如果已存在同名文件，则不再下载
						f = new File(localDir + files[0].getName());
						if (f.exists()) {
							logger.error("本地文件已存在同名文件" + rs + "，跳过");
							continue;
						}
						out = new FileOutputStream(f);

						if (ftpClient.retrieveFile(formatRemoteFileName, out))
							downloadCount++;
					} finally {
						if (out != null) {
							try {
								out.close();
							} catch (Exception e) {
							}
						}
						out = null;
						f = null;
						files = null;
						formatRemoteFileName = null;
					}
				}

				if (downloadCount == 0) {
					logger.error("没有需要下载的远程文件，跳过");
					return DownloadStatus.Remote_File_Noexist;
				}

			} finally {
				out = null;
				f = null;
				files = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new FTPDownloadException(e.getMessage());
		}
		return DownloadStatus.Download_New_Success;
	}

	/**
	 * 下载远程服务器上指定目录下所有文件，子目录及子目录下文件不下载 保存到指定位置
	 * 
	 * @param remoteDir
	 * @param localDir
	 * @return
	 * @throws IOException
	 */
	public DownloadStatus downloadFilesInDir(String remoteDir, String localDir)
			throws FTPDownloadException {
		List<String> namelist = new ArrayList<String>();
		try {
			FTPFile[] files = ftpClient.listFiles(new String(remoteDir
					.getBytes(this.ftpConfig.getDefaultLocalCharset()),
					this.ftpConfig.getDefaultRemoteCharset()));

			if (files != null && files.length > 0) {
				for (FTPFile f : files) {
					if (f.isFile())
						namelist.add(f.getName());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new FTPDownloadException(e.getMessage());
		}
		return this.downloadFile(namelist.toArray(new String[] {}), localDir);
	}

	/**
	 * 上传文件
	 * 
	 * @param localFile
	 *            要上传的本地文件全路径
	 * @param remoteDir
	 *            远程保存路径
	 * @return
	 * @throws IOException
	 */
	public UploadStatus uploadFile(String[] localFile, String remoteDir)
			throws FTPUploadException {
		try {
			UploadStatus status = UploadStatus.Upload_New_File_Failed;
			this.setFtpClientMode();
			File file = null;
			FileInputStream localIn = null;
			// 获得实际的远程目录保存路径
			String realRemoteDir = StringUtils.isNotEmpty(remoteDir) ? remoteDir
					: this.ftpConfig.getRemotedir();
			realRemoteDir = realRemoteDir.endsWith(File.separator) ? realRemoteDir
					: realRemoteDir + File.separator;
			status = this.CreateDirecroty(realRemoteDir, ftpClient);
			if (status != UploadStatus.Create_Directory_Success) {
				return status;
			}
			int uploadCount = 0; // 上传成功计数器
			try {
				if (localFile == null || localFile.length == 0) {
					logger.error("不存在本地要上传文件，上传中止");
					return UploadStatus.Local_File_Noexist;
				}
				for (String fs : localFile) {
					try {
						file = new File(fs);
						if (!file.exists() || !file.isFile()) {
							logger.error("本地文件" + localFile + "不存在或该文件是目录，跳过");
							continue;
						}
						localIn = new FileInputStream(file);
						// 设置以二进制方式传输
						ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
						// 按同名文件上传
						if (ftpClient
								.storeFile(
										new String(
												file.getName()
														.getBytes(
																this.ftpConfig
																		.getDefaultLocalCharset()),
												this.ftpConfig
														.getDefaultRemoteCharset()),
										localIn))
							uploadCount++; // 累计上传成功的
					} finally {
						file = null;
						if (localIn != null) {
							try {
								localIn.close();
							} catch (Exception e) {
							}
						}
						localIn = null;
					}
				}
				if (uploadCount == 0) {
					logger.error("没有本地要上传文件，上传中止");
					return UploadStatus.Local_File_Noexist;
				}
			} finally {
				file = null;
				localIn = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new FTPUploadException(e.getMessage());
		}
		return UploadStatus.Upload_New_File_Success;
	}

	/**
	 * 递归创建远程服务器目录
	 * 
	 * @param remote
	 *            远程服务器文件绝对路径
	 * @param ftpClient
	 *            FTPClient对象
	 * @return 目录创建是否成功
	 * @throws IOException
	 */
	public UploadStatus CreateDirecroty(String remoteFileDir,
			FTPClient ftpClient) throws IOException {
		UploadStatus status = UploadStatus.Create_Directory_Success;
		String directory = remoteFileDir;

		if (!directory.equalsIgnoreCase(File.separator)
				&& !ftpClient.changeWorkingDirectory(new String(directory
						.getBytes(this.ftpConfig.getDefaultLocalCharset()),
						this.ftpConfig.getDefaultRemoteCharset()))) {
			// 如果远程目录不存在，则递归创建远程服务器目录
			int maxIndex = 20;// 最大创建层级数
			int start = 0;
			int end = 0;
			if (directory.startsWith(File.separator)) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf(File.separator, start);
			while (true) {
				String subDirectory = new String(remoteFileDir.substring(start,
						end).getBytes(this.ftpConfig.getDefaultLocalCharset()),
						this.ftpConfig.getDefaultRemoteCharset());
				if (!ftpClient.changeWorkingDirectory(subDirectory)) {
					if (ftpClient.makeDirectory(subDirectory)) {
						ftpClient.changeWorkingDirectory(subDirectory);
					} else {
						logger.error("创建目录失败");
						return UploadStatus.Create_Directory_Fail;
					}
				}

				start = end + 1;
				end = directory.indexOf(File.separator, start);

				// 检查所有目录是否创建完毕
				if (end <= start) {
					break;
				}
				if (maxIndex == 0)
					break;

				maxIndex--;
			}
		}
		return status;
	}
}
